import '@hw-agconnect/auth-ohos';
import Long from 'long';
import prompt from '@ohos.promptAction';
import { Post } from '../models/Post';
import { AGConnectCloudDB, CloudDBZone, CloudDBZoneConfig, CloudDBZoneQuery } from '@hw-agconnect/database-ohos';
import { getAGConnect } from './AgcConfig';
import { Log } from '../common/Log';

const TAG = "[AGCCloudDB]";
const JSON_FILE_NAME = "schema.json";

export class AGCCloudDB {
  static _instance: AGCCloudDB;

  private cloudDbZone: CloudDBZone;

  constructor(context) {
    getAGConnect(context);
  }

  static instance(context) {
    if (!AGCCloudDB._instance) {
      AGCCloudDB._instance = new AGCCloudDB(context);
    }
    return AGCCloudDB._instance;
  }

  public async init(context: any) {
    try {
      await AGConnectCloudDB.initialize(context);
      const agcCloudDB = await AGConnectCloudDB.getInstance();
      const value = await context.resourceManager.getRawFile(JSON_FILE_NAME);
      let json = "";
      for (var i = 0; i < value.length; i++) {
        json += String.fromCharCode(value[i]);
      }
      const objectTypeInfo = JSON.parse(json);
      agcCloudDB.createObjectType(objectTypeInfo);
      this.cloudDbZone = await agcCloudDB.openCloudDBZone(new CloudDBZoneConfig("Demo"));
    } catch (e) {
      Log.error(TAG, "init error " + JSON.stringify(e));
    }
  }

  public orderByDate(): Promise<Post[]> {
    return new Promise<Post[]>((resolve, reject) => {
      const query = CloudDBZoneQuery.where(Post).orderByDesc("insertDate").limit(100, 0);
      this.cloudDbZone.executeQuery(query).then((ret) => {
        Log.info(TAG, "query post by date success " + ret.getSnapshotObjects());
        resolve(ret.getSnapshotObjects());
      }).catch((error) => {
        Log.error(TAG, "query post by date error " + JSON.stringify(error));
        reject(error);
      });
    })
  }

  public orderByMostLiked(): Promise<Post[]> {
    return new Promise<Post[]>((resolve, reject) => {
      const query = CloudDBZoneQuery.where(Post).orderByDesc("likeCount").limit(100, 0);
      this.cloudDbZone.executeQuery(query).then((ret) => {
        Log.info(TAG, "query post by like success " + ret.getSnapshotObjects());
        resolve(ret.getSnapshotObjects());
      }).catch((error) => {
        Log.error(TAG, "query post by like error " + JSON.stringify(error));
        reject(error);
      });
    })
  }

  public getMyPosts(uid: string, sortPosts?: boolean, includeLikes?: boolean): Promise<Post[]> {
    return new Promise<Post[]>((resolve, reject) => {
      const query = CloudDBZoneQuery.where(Post).equalTo("userId", uid).orderByDesc("insertDate");
      this.cloudDbZone.executeQuery(query).then((ret) => {
        Log.info(TAG, "query my post success " + ret.getSnapshotObjects());
        resolve(ret.getSnapshotObjects());
      }).catch((error) => {
        Log.error(TAG, "query my post error " + JSON.stringify(error));
        reject(error);
      })
    })
  }

  public getMyPostsByLike(uid: string, sortPosts?: boolean, includeLikes?: boolean): Promise<Post[]> {
    return new Promise<Post[]>((resolve, reject) => {
      const query = CloudDBZoneQuery.where(Post).equalTo("userId", uid).orderByDesc("likeCount").limit(100, 0);
      this.cloudDbZone.executeQuery(query).then((ret) => {
        Log.info(TAG, "query my post by likes success " + ret.getSnapshotObjects());
        resolve(ret.getSnapshotObjects());
      }).catch((error) => {
        Log.error(TAG, "query my post by likes error " + JSON.stringify(error));
        reject(error);
      })
    })
  }

  public getInitialRecords(): Promise<Post[]> {
    return new Promise<Post[]>((resolve, reject) => {
      const query = CloudDBZoneQuery.where(Post).orderByDesc("insertDate");
      try {
        this.cloudDbZone.executeQuery(query).then((ret) => {
          Log.info(TAG, "getInitial post success " + ret.getSnapshotObjects());
          resolve(ret.getSnapshotObjects());
        }).catch((error) => {
          Log.error(TAG, "getInitial post error " + JSON.stringify(error));
          reject(error);
        })
      } catch (e) {

      }
    })
  }

  // if parameter has id, it works as update method
  public upsertRecord(post: Post): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      post.setInsertDate(new Date(post.getInsertDate()));
      this.cloudDbZone.executeUpsert(post).then((ret) => {
        Log.info(TAG, "upsert my post success " + ret);
        resolve();
      }).catch((error) => {
        Log.error(TAG, "upsert my post failed " + JSON.stringify(error));
        reject(error);
      });
    })
  }

  public deleteRecord(post: Post, uid: string): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      if (post.userId === uid) {
        this.cloudDbZone.executeDelete(post).then((ret) => {
          Log.info(TAG, "delete my post success " + ret);
          resolve();
        }).catch((error) => {
          Log.error(TAG, "delete my post error " + JSON.stringify(error));
          reject(error);
        })
      } else {
        prompt.showToast({ message: 'you can only delete your post' });
      }
    })
  }

  public likePost(id: Long, userId: string): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      const query = CloudDBZoneQuery.where(Post).equalTo("id", id);
      this.cloudDbZone.executeQuery(query).then((ret) => {
        Log.info(TAG, "query post by id success " + ret.getSnapshotObjects());
        if (ret.getSnapshotObjects().length > 0) {
          const post = ret.getSnapshotObjects()[0];
          const likes = JSON.parse(post.getLikes());
          if (likes.indexOf(userId) < 0) {
            likes.push(userId);
            post.setLikes(JSON.stringify(likes));
            post.setLikeCount(likes.length);
            this.cloudDbZone.executeUpsert(post).then((ret) => {
              Log.info(TAG, "update likeCount success ", ret);
              resolve();
            }).catch((error) => {
              Log.error(TAG, "update likeCount failed " + JSON.stringify(error));
              reject();
            });
          }
        }
      }).catch((error) => {
        Log.error(TAG, "query post by id failed " + JSON.stringify(error));
        reject();
      });
    })
  }

  public unlikePost(id: Long, userId: string): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      const query = CloudDBZoneQuery.where(Post).equalTo("id", id);
      this.cloudDbZone.executeQuery(query).then((ret) => {
        Log.info(TAG, "query post by id success " + ret.getSnapshotObjects());
        if (ret.getSnapshotObjects().length > 0) {
          const post = ret.getSnapshotObjects()[0];
          let likes = JSON.parse(post.getLikes());
          if (likes.indexOf(userId) >= 0) {
            likes = likes.filter(item => item !== userId);
            Log.info(TAG, "unlike by id success " + JSON.stringify(likes));
            post.setLikes(JSON.stringify(likes));
            post.setLikeCount(likes.length);
            this.cloudDbZone.executeUpsert(post).then((ret) => {
              Log.info(TAG, "update unlikeCount success ", ret);
              resolve();
            }).catch((error) => {
              Log.error(TAG, "update likeCount failed " + JSON.stringify(error));
              reject();
            });
          }
        }
      }).catch((error) => {
        Log.error(TAG, "query post by id failed " + JSON.stringify(error));
        reject();
      });
    })
  }
}